package org.hyena.cvms.aaatest;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.springframework.expression.ExpressionException;

/**
 * 运算工具类，解析字符串运算式
 * @author PI2-0137
 * @date 2021年9月30日 上午9:44:22
 */
public final class CalculationUtils2 {
    
    /**
     * 获取表达式的double值
     * <p>可用括号,如 9-(2*5-2)+8/4 
     * <p>实际上利用了'正则'来去除括号,耗时比‘递归’去括号约多1/3
     * @author: PI2-0137
     * @date 2021年10月8日 下午4:19:45
     * @param expr
     * @return
     * @version
     */
    public static Double getDouble(String expr) {
    	expr = expr.replaceAll("\\s*|\\t|\\r|\\n", "");
   	    int a = expr.indexOf("("); 
   	    // 去括号处理
   	    if(a > -1) {
   	    	if(a == 0)expr = expr.substring(1,expr.length()-1);
   	    	String regEx="\\([0-9\\.\\*\\+-]+\\)";  
   	    	Pattern p = Pattern.compile(regEx);
   	    	a = expr.length()/2;
   	    	while(true) {
   	    		Matcher m = p.matcher(expr);
   	    		int count = 0;
   	    		a --;
   	    		if(a < 0 )break;
   	    		while(m.find()) {
   	   	    		String tem = m.group(0);
   	   	    		if(tem.length() < 3)return null;
   	   	    	    expr = expr.replace(tem,""+computeNumber(tem.substring(1, tem.length()-1)));
   	   	    		count ++;
   	   	    	}
   	    		if(count < 1)break;
   	    	}
   	    }
   	    return computeNumber(expr);
    }
    
	public static Double getDouble(String expr,int scale) {
		return round(getDouble(expr),scale);
	}
    
	/**
	 * 表达式必须是boolean类型的，否则报错
	 * @author: PI2-0137
	 * @date 2021年10月8日 上午8:55:10
	 * @param calculationString
	 * @return
	 * @version
	 */
	public static Boolean getBoolean(String expr) {
		Object res = getObject(expr);
		if(res != null && res instanceof Boolean)return (Boolean)res;
		throw new ExpressionException("表达式异常: 不是比较算式");
	}
	
	/**
	 * 实际上就是返回 {@link #getObject(String)}的toString
	 * @author: PI2-0137
	 * @date 2021年10月8日 下午4:32:41
	 * @param expr
	 * @return
	 * @version
	 */
	public static String getString(String expr) {
		Object res = getObject(expr);
		if(res != null)return res.toString();
		return null;
	}
	
	/**
	 * 根据运算式，可能返回boolean、double
	 * @author: PI2-0137
	 * @date 2021年10月8日 下午4:31:47
	 * @param expr
	 * @return
	 * @version
	 */
	private static Object getObject(String expr) {
		int cou = 0;
	    String regx = null;
	    
	    if(expr.indexOf(">=")>-1) {cou++;regx = ">=";}
	    else if(expr.indexOf(">")>-1) {cou++;regx = ">";}
	    
	    if(expr.indexOf("<=")>-1) 
	       {cou++;regx = "<=";}
	    else if(expr.indexOf("<")>-1) 
	       {cou++;regx = "<";}
	    
	    if(expr.indexOf("==")>-1) {cou++;regx = "==";}
	    
	    if(cou == 0) { // 返回数字
	    	return getDouble(expr);
	    }else if(cou == 1) {  // 返回boolean
	    	String[] two = expr.split(regx);
			if(two == null || two.length != 2) {
				throw new ExpressionException("表达式异常: 缺少比较方");
			}
			return compareBoolean(getDouble(two[0]),regx,getDouble(two[1]));
	    }
	    throw new ExpressionException("表达式异常: 只支持一个比较符号");
	}
	
	public static double round(double v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The   scale   must   be   a   positive   integer   or   zero");
        }
        java.math.BigDecimal b = new java.math.BigDecimal(Double.toString(v));
        java.math.BigDecimal one = new java.math.BigDecimal("1");
        return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
    }
	
    /**
     * 计算无括号算式，如 1+2*5-8 。只返回null或double。算式异常时返回null
     * @author: PI2-0137
     * @date 2021年10月8日 上午11:45:56
     * @param a
     * @return
     * @version
     */
    private static Double computeNumber(String a) {
    	if(a == null)return null;
    	char[] chars = a.toCharArray();
    	Double tem = null,m1=null;
    	int f=0;
    	char ty = 0,f1=0,f2=0; // 1+ 2- 3 * 4/
    	for(int i=0;i<chars.length;i++) {
    		char t = chars[i];
    		if(t<42 || t== 44 || t > 57)throw new ExpressionException("出现了不应该出现的字符："+t);; // 只允许的符号   0至9 + - . / * 
    		if(t < 48 && t != 46 && i > 0 && chars[i-1] > 47 && chars[i-1] < 58) { // + - * /
    			if(t=='-' || t=='+') {
    				if(m1 != null) {
    					Double ddd= Double.valueOf(String.valueOf(chars, f, i-f));
        				if(f1 > 0) {
        					tem =  computeNormal(f1,tem,computeNormal(f2,m1,ddd));
        				}else {
        					tem = computeNormal(f2,m1,ddd);
        				}
        				m1 = null;
        			}else if(f1=='-') {
        				tem =tem - Double.valueOf(String.valueOf(chars, f, i-f));
        			}else if (f1 == '+') {
        				tem = tem + Double.valueOf(String.valueOf(chars, f, i-f));
        			}else { // 首次
        				tem = Double.valueOf(String.valueOf(chars, f, i-f));
        			}
        			f1 = t;
    			}else {
    				if(m1 != null) {
    					m1 = computeNormal(f2,m1,Double.valueOf(String.valueOf(chars, f, i-f)));
        			}else {
        				m1 = Double.valueOf(String.valueOf(chars, f, i-f));
        			}
    				f2 = t;
    			}
    			ty = t;
    			f = i + 1;
    		}
    		if(i == chars.length-1) {
    			Double ddd= Double.valueOf(String.valueOf(chars, f,  i-f+1));
    			if(f == 0)return ddd;
    			if(m1 != null) {
    				if(f1 > 0) {
    					return computeNormal(f1,tem,computeNormal(f2,m1,ddd));
    				}
    				return computeNormal(f2,m1,ddd);
    			}
    			return computeNormal(ty,tem,ddd);
    		}
    	}
    	throw new ExpressionException("错误的表达式");
    }
    
    /**
     * 比较两个double数据大小,需要传入字符型的 “==” , ">=" , ">" 等
     * @author: PI2-0137
     * @date 2021年9月30日 下午5:36:38
     * @param v1
     * @param tag
     * @param v2
     * @return
     * @version
     */
    private static Boolean compareBoolean(double v1,String tag,double v2) {
    	if("==".equals(tag)) {
			return v1 == v2; // == 比较的对象必须是基本数据类型
		}else if(">=".equals(tag)) {
			return v1 >= v2;
		}else if("<=".equals(tag)) {
			return v1 <= v2;
		}else if(">".equals(tag)) {
			return v1 > v2;
		}else if("<".equals(tag)) {
			return v1 < v2;
		}else {
			return false;
		}
    }
    
    private static Double computeNormal(char sign,Double d1,Double d2) {
    	if(d1==null || d2== null)return null;
    	switch(sign) {
    	    case '-':
    	    	return d1 - d2;
    	    case '+':
    	    	return d1 + d2;
    	    case '*':
    	    	return d1 * d2;
			case '/':
				return d1 / d2;
			default :
				return null;
    	}
    }
}
